﻿// Zhang-Suen.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

bool TestAndSet(Mat& bin) {
	const static array<array<int, 2>, 10> pairs{ { {0,0},{0,0},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1} } };

	auto bin_ptr = [&](int i, int j, int n) {
		return bin.ptr<uchar>(i + pairs[n][0])[j + pairs[n][1]];
	};
	auto func_A = [&](int i, int j) {
		int count = 0;
		uchar prev = bin.ptr<uchar>(i + pairs.back()[0])[j + pairs.back()[1]];
		for (int n = 2; n <= 9; n++) {
			uchar now = bin_ptr(i, j, n);
			if (prev != 0 && now == 0) count++;
			prev = now;
		}
		return count == 1;
	};
	auto func_B = [&](int i, int j) {
		int count = 0;
		for (int n = 2; n <= 9; n++) {
			if (bin_ptr(i, j, n) == 0)
				count++;
		}
		return 2 <= count && count <= 6;
	};

	bool set = false;

	Mat thinning;
	thinning = Mat::zeros(bin.rows, bin.cols, CV_8UC1);
	cv::parallel_for_(Range(0, bin.cols * bin.rows), [&](const Range& range) {
		for (int r = range.start; r < range.end; r++) {
			int i = r / bin.cols;
			int j = r % bin.cols;
			if (i != 0 && i != bin.rows - 1 && j != 0 && j != bin.cols - 1
				&& bin.ptr<uchar>(i)[j] == 0
				&& 0 + bin_ptr(i, j, 2) + bin_ptr(i, j, 4) + bin_ptr(i, j, 6) != 0
				&& 0 + bin_ptr(i, j, 4) + bin_ptr(i, j, 6) + bin_ptr(i, j, 8) != 0
				&& func_A(i, j)
				&& func_B(i, j)) {
				thinning.ptr<uchar>(i)[j] = 255;
				set = true;
			}
		}
		});
	bitwise_or(thinning, bin, bin);
	thinning.setTo(0);
	cv::parallel_for_(Range(0, bin.cols * bin.rows), [&](const Range& range) {
		for (int r = range.start; r < range.end; r++) {
			int i = r / bin.cols;
			int j = r % bin.cols;
			if (i != 0 && i != bin.rows - 1 && j != 0 && j != bin.cols - 1
				&& bin.ptr<uchar>(i)[j] == 0
				&& 0 + bin_ptr(i, j, 2) + bin_ptr(i, j, 4) + bin_ptr(i, j, 8) != 0
				&& 0 + bin_ptr(i, j, 2) + bin_ptr(i, j, 6) + bin_ptr(i, j, 8) != 0
				&& func_A(i, j)
				&& func_B(i, j)) {
				thinning.ptr<uchar>(i)[j] = 255;
				set = true;
			}
		}
		});
	bitwise_or(thinning, bin, bin);
	return set;
}

int main()
{
	Mat src = imread(R"(C:\Users\scutb\Desktop\1.jpg)");
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	Mat bin;
	threshold(gray, bin, 127, 255, THRESH_BINARY_INV);

	cv::imshow("bin", bin);
	
	while (TestAndSet(bin)) {};

	cv::imshow("thin", bin);
	cv::waitKey(0);
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
